//enemy always win
package src{

import flash.display.*;
import flash.events.*;
import flash.utils.getTimer;

public class Ai extends MovieClip{
	var boardCol:BoardCol;
	var resultBo:Board;
	var winnerArr:Array;
	//(new revised 8feb16) associated with empty array in BoardCol.. contains {chance, board}.. chance is the winning chance of enemy (count, not percent)
	var winnerArrBest:WinData;	//the (chance, board) of best winning chance
	var logStr:String;
	var forceStopCalc:Boolean;
	var allPossibleMove:Number;
	var isFullAI:Boolean;
	var startTime:Number;
	
	public function get timeStopThreshold():Number{
		return (isFullAI?60000:15000);
	}
	public function Ai(bc:BoardCol, isFullAI:Boolean){
		this.isFullAI = isFullAI;
		boardCol = bc;
		logStr = "";
		log("---Starting AI---");
		var newBc:BoardCol
		var bo:Board;
		
		//if enemy can win in 1 step, do it
		for(i=0;i<bc.getEmpty.length;i++){
			//check every possibilities
			newBc = new BoardCol(bc);
			bo = newBc.getEmpty[i];
			newBc.setOwner(bo,"en");
			if(newBc.checkWinner() == "en"){
				resultBo = bo;
				log("Enemy will win, do it : "+resultBo.print());
				return;
			}
		}
		//if player will win in current step, block immediately
		for(i=0;i<bc.getEmpty.length;i++){
			//check every possibilities
			newBc = new BoardCol(bc);
			bo = newBc.getEmpty[i];
			newBc.setOwner(bo,"pl");
			if(newBc.checkWinner() == "pl"){
				resultBo = bo;
				log("Player will win in the next step, block it : "+resultBo.print());
				return;
			}
		}
		
		//if the center board is still empty, fill it.. this can also minimize the usage of minmax in early game, to fasten
		var dim:Number = bc.getAll.length;
		function fillBoard(cBo:Board,info:String = "Center"):Boolean{ //cbo : center board or corner board to be filled
			if(cBo.getOwner == ""){
				if(bc.cannotCreateFullPath(cBo)){
					//also check, is filling this board have potential to create full path for enemy? if not, no need
					return false;
				}
				resultBo = cBo;
				log(info+" board is empty, enemy will fill it");
				return true;
			}else{
				return false;
			}
		}
		var cen:Number, cen0:Number, cen1:Number, cen2:Number;
		var centerBoA:Array;
		if(dim == 3){
			//for 3x3 dimensions
			cen = Math.floor(dim/2);
			if(fillBoard(bc.getAll[cen][cen])){
				return;
			}
		}else if(dim == 5){
			//for 5x5, the center will be 9 board in the middle
			//prioritize the centermost
			cen = Math.floor(dim/2);
			if(fillBoard(bc.getAll[cen][cen])){
				return;
			}
			
			cen0 = cen-1;
			cen1 = cen;
			cen2 = cen+1;
			centerBoA = [bc.getAll[cen0][cen0],bc.getAll[cen0][cen1],bc.getAll[cen0][cen2],
						bc.getAll[cen1][cen0],bc.getAll[cen1][cen1],bc.getAll[cen1][cen2],
						bc.getAll[cen2][cen0],bc.getAll[cen2][cen1],bc.getAll[cen2][cen2]];
			for(i=0;i<centerBoA.length;i++){
				if(fillBoard(centerBoA[i])){
					return;
				}
			}
			
		}else{
			//for even dimensions, check all 4 center board
			cen0 = Math.round(dim/2)-1;
			cen1 = Math.round(dim/2);
			centerBoA = [bc.getAll[cen0][cen0],bc.getAll[cen0][cen1],
						bc.getAll[cen1][cen0],bc.getAll[cen1][cen1]];
			for(i=0;i<centerBoA.length;i++){
				if(fillBoard(centerBoA[i])){
					return;
				}
			}
		}
		//also check corner boards, prior it, after filling the centers (for 4x4 and 5x5 only, to fasten)
		if(dim > 3){
			var cornerBoA:Array = [bc.getAll[0][0],bc.getAll[0][dim-1],
								bc.getAll[dim-1][0],bc.getAll[dim-1][dim-1]];
			for(i=0;i<cornerBoA.length;i++){
				if(fillBoard(cornerBoA[i],"Corner")){
					return;
				}
			}
		}
		
		//calculating
		winnerArrBest = null;
		winnerArr = new Array();
		forceStopCalc = false;
		allPossibleMove = bc.getAllPossibleMove;
		startTime = getTimer();
		
		//array of object consisting of {board, winner}
		newBc = new BoardCol(bc);
		calcResult(newBc,"en");
		var obj:WinData;
		
		//trace all the winning solutions
		for(var i=0;i<winnerArr.length;i++){
			obj = winnerArr[i];
			log("Chance to win if enemy moves to "+obj.board.print()+" : "+obj.getChance(allPossibleMove)+"%");
		}

		//from the winnerArr data, check when will the enemy win, pick the fewest step
		if(winnerArrBest != null){
			obj = winnerArrBest;
			resultBo = obj.board;
			log("Enemy best chance to win is if move to "+resultBo.print());
			return;
		}
		//winner not found, because player is too good
		log("Enemy cannot win, player is too good");
		resultBo = null;
	}
	
	function calcResult(bc:BoardCol,curTurn:String,wd:WinData=null):void{
		if(forceStopCalc) return;
		var wdOri:WinData = wd;
		if(winnerArrBest != null){
			//trace(winnerArrBest.chance);
		}
		//winnerArrBest.chance >= stopThreshold
		if(winnerArrBest != null && getTimer()-startTime >= timeStopThreshold){
			//if the current best solution is more than threshold, stop it, it's considered good
			forceStopCalc = true;
			log("Enemy AI is force stopped after "+(timeStopThreshold/1000)+" sec, using current best solution");
			return;
		}
		if(wd != null){
			var winner:String = bc.checkWinner();
			if(winner != ""){
				//winner is assigned, stop this recursive
				if(winner == "en"){
					//increase the win chance
					wd.chance ++;
					//trace("chance "+wd.chance);
					if(winnerArrBest == null || winnerArrBest.chance<wd.chance){
						//change the best if this have higher chance
						winnerArrBest = wd;
					}
				}
				return;
			}
			if(bc.getEmpty.length == 0){
				//no more empty board, stop
				return;
			}
		}
		//the en or pl clicks will simulate clicking each empty board
		var emptyArr:Array = bc.getEmptyGood(curTurn);
		if(wdOri == null){
			for(var i=0;i<emptyArr.length;i++){
				//if enemy will never win if moves to this board, do not use this, useless
				//means, if enemy cannot create full path in with this board
				var bo:Board = emptyArr[i];
				if(bc.cannotCreateFullPath(bo)){
					log("Bypassing board "+bo.print()+", full path is not possible");
					emptyArr.splice(i,1); i--;
					//trace("remain "+emptyArr.length);
				}
			}
		}
		var newBc:BoardCol;
		for(i=0;i<emptyArr.length;i++){
			if(forceStopCalc) break;
			newBc = new BoardCol(bc);
			bo = newBc.retrieveEmptyGood(emptyArr)[i];
			newBc.setOwner(bo,curTurn);
			//switch the turn
			var ct:String = (curTurn == "pl"?"en":"pl");
			var startBoard:Board, step:Number;
			if(wdOri == null){
				//means it's starting
				startBoard = bo;
				wd = new WinData(startBoard,0);
				winnerArr.push(wd);
			}else{
				startBoard = wd.board;
			}
			
			//trace("winnerArr count = "+winnerArr.length);
			calcResult(newBc,ct,wd);
		}
	}
	
	function log(str:String):void{
		logStr += str+"\n";
	}
	public function get getLog():String{
		return Mi.trim(logStr);
	}
	public function get getResult(){
		return resultBo;
	}
	
	
	
}

}